package com.yichat.micro.msghttpservice.core.retry;

import com.alibaba.fastjson.annotation.JSONField;
import com.yichat.micro.msghttpservice.common.Constants;
import com.yichat.micro.msghttpservice.common.util.JsonUtil;
import com.yichat.micro.msghttpservice.common.util.StringUtil;
import com.yichat.micro.msghttpservice.common.vo.ChildMsgHttpRequestVo;
import com.yichat.micro.msghttpservice.common.vo.MsgHttpRequestVo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.List;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * Created by chenmohan on 2019/04/15
 *
 * @description 在接受传参的MsgHttpRequest后，这个对象将其包装成RetryMsgHttpRequestVo,提供能够记录重试上下文的字段
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RetryMsgHttpRequestVo implements Delayed {

    private MsgHttpRequestVo vo;

    /**
     * http请求时间
     */
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date requestDatetime;

    /**
     * 当前重试次数
     */
    private int currentRetryTimes = 0;

    /**
     * 当前延时时长
     */
    private long currentDelay;

    /**
     * 如果是一个批量请求列表,该字段表示当前请求到了第几个
     */
    private int currentListIndex = -1;

    /**
     * 本次重试的超时时间,超时后会重新调用请求
     */
    long expire;

    /**
     * 记录准备重试请求的全局id
     */
    private long sid;

    /**
     * 批量发送请求的集合列表
     */
    transient private List<ChildMsgHttpRequestVo> list;

    private Integer listSize;

    /**
     * 是否是批量请求
     */
    private boolean batch = false;


    public RetryMsgHttpRequestVo(MsgHttpRequestVo vo) {
        this.vo = vo;
    }

    /**
     * 通过{@link MsgHttpRequestVo}来包装成1个重试请求对象RetryMsgHttpRequestVo,
     *
     * @param vo
     * @return
     */
    public static RetryMsgHttpRequestVo buildRetryVo(MsgHttpRequestVo vo, long sid) {
        RetryMsgHttpRequestVo retryVo = new RetryMsgHttpRequestVo(vo);
        retryVo.setSid(sid);
        retryVo.init();
        return retryVo;
    }

    /**
     * 通过{@link RetryMsgHttpRequestVo}来组装RetryMsgHttpRequestVo
     *
     * @param retryVo
     * @return
     */
    static RetryMsgHttpRequestVo buildRetryVo(RetryMsgHttpRequestVo retryVo) {
        retryVo.retryInfo();
        return retryVo;
    }

    /**
     * 根据上次的重试上下文生成当前的重试上下文信息,在buildRetryVo(RetryMsgHttpRequestVo retryVo)被调用
     */
    private void retryInfo() {
        this.currentRetryTimes++;
        long delay = vo.getTimeout() * (currentRetryTimes - 1);
        this.currentDelay = delay < 1 ? vo.getTimeout() : delay;
    }

    /**
     * 初始化重试请求的上下文基本信息(重试次数、请求失败后再次调用间隔)
     */
    private void init() {
        this.currentRetryTimes = 0;
        this.requestDatetime = new Date();  //设置http请求时间
        currentDelay = vo.getTimeout();
        //将批量请求的json字符串解析到data字段中
        if (StringUtil.isNotEmpty(this.vo.getList())) {
            List<ChildMsgHttpRequestVo> data = JsonUtil.toList(this.vo.getList(), ChildMsgHttpRequestVo.class);
            this.list = data;
            this.currentListIndex = 0;  //将要批量发送的数据指向第一个
            this.batch = true;
            this.vo.setSync(Constants.enalbe);  //批量请求只能同步请求
            this.listSize = this.list.size();
        }

    }

    /**
     * 在批量请求数组集合中,将当前要请求的对象移动到下一个子请求【调用此方法返回true时会修改索引值】
     *
     * @return 返回true表示还有下一个子请求待发送, fasle表示所有子请求已全部请求完成
     */
    public boolean next() {
        this.currentListIndex++;
        if (this.currentListIndex < this.list.size()) {
            this.currentRetryTimes = 0; //重置当前重试次数
            this.requestDatetime = new Date();  //请求时间修改
            return true;
        }
        this.currentListIndex--;
        return false;
    }


    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }
}
